<?php


namespace App\Services;


use App\Exceptions\CouponCodeUnavailableException;
use App\Exceptions\InvalidRequestException;
use App\Jobs\CloseOrder;
use App\Models\Brand;
use App\Models\Category;
use App\Models\CouponCode;
use App\Models\Order;
use App\Models\Product;
use App\Models\ProductSku;
use App\Models\User;
use App\Models\UserAddress;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class OrderService
{
    // 创建订单
    public function store(User $user, UserAddress $address, $remark, $items, CouponCode $coupon = null)
    {
        // 如果传入了优惠券
        if ($coupon) {
            // 判断优惠券类型
            $coupon->checkAvailable($user);
        }

        // 开启事务
        $order = DB::transaction(function () use ($user, $address, $remark, $items, $coupon) {
            // 更新地址最后使用时间
            $address->update([
                'last_used_at' => Carbon::now()
            ]);

            // 创建一个订单
            $order = new Order([
                'address' => [
                    'address' => $address->full_address,
                    'zip' => $address->zip,
                    'contact_name' => $address->contact_name,
                    'contact_phone' => $address->contact_phone
                ],
                'remark' => $remark, // 订单备注
                'total_amount' => 0
            ]);

            $order->user()->associate($user->id);
            $order->save();

            $totalAmount = 0; // 计算订单总价格

            $productGroup = [];
            $categoryGroup = [];
            $brandGroup = [];

            foreach ($items as $item) {
                $sku = ProductSku::find($item['sku_id']);

                // $order->items()->make() 可以新建一个关联关系的对象（也就是 OrderItem）但不保存到数据库
                $orderItem = $order->items()->make([
                    'price' => $sku->price,
                    'amount' => $item['amount'],
                ]);
                $orderItem->product()->associate($sku->product);
                $orderItem->productSku()->associate($sku);
                $orderItem->save();

                $totalAmount += $sku->price * $item['amount'];

                $productGroup[$sku->product_id] = $productGroup[$sku->product_id] ?? 0;
                $productGroup[$sku->product_id] += $sku->price * $item['amount'];

                $brandGroup[$sku->product->brand_id] = $brandGroup[$sku->product->brand_id] ?? 0;
                $brandGroup[$sku->product->brand_id] += $sku->price * $item['amount'];

                $categoryGroup[$sku->product->category_id] = $categoryGroup[$sku->product->category_id] ?? 0;
                $categoryGroup[$sku->product->category_id] += $sku->price * $item['amount'];

                // 减库存: decreaseStock() 方法里我们用了 decrement() 方法来减少字段的值，该方法会返回影响的行数。
                if ($sku->decreaseStock($item['amount']) <= 0) {
                    throw new InvalidRequestException('该商品库存不足');
                }
            }

            if ($coupon) {
                // 校验是否符合优惠券设定
                switch ($coupon->coupon_codeable_type) {
                    case Brand::class:  // 品牌优惠券
                        if (!in_array($coupon->coupon_codeable_id, array_keys($brandGroup))
                            && !$coupon->checkAvailableReturnBoolean($user, $brandGroup[$coupon->coupon_codeable_id])) {
                            throw new CouponCodeUnavailableException('使用优惠券失败');
                        }
                        break;
                    case Product::class:  // 商品优惠券
                        if (!in_array($coupon->coupon_codeable_id, array_keys($productGroup))
                            && !$coupon->checkAvailableReturnBoolean($user, $productGroup[$coupon->coupon_codeable_id])) {
                            throw new CouponCodeUnavailableException('使用优惠券失败');
                        }
                        break;
                    case Category::class: // 分类优惠券
                        if (!in_array($coupon->coupon_codeable_id, array_keys($categoryGroup))
                            && !$coupon->checkAvailableReturnBoolean($user, $categoryGroup[$coupon->coupon_codeable_id])) {
                            throw new CouponCodeUnavailableException('使用优惠券失败');
                        }
                        break;
                    case "":  // 全场优惠券
                        $coupon->checkAvailable($user, $totalAmount);
                        break;
                    default:
                        throw new InvalidRequestException("未知优惠券多态类型:" . $coupon->coupon_codeable_type);
                        break;
                }

                // 把订单金额修改为优惠后的金额
                $totalAmount = $coupon->getAdjustedPrice($totalAmount);
                // 订单和优惠券进行关联
                $order->couponCode()->associate($coupon);
                // 增加优惠券的用量, 需要判断返回值
                if ($coupon->changeUsed() <= 0) {
                    throw new CouponCodeUnavailableException('该优惠券已被兑换完毕');
                }
            }

            // 更新订单总金额
            $order->update([
                'total_amount' => $totalAmount
            ]);

            return $order;
        });

        // 如果半小时没有支付订单, 触发关闭订单任务
        dispatch(new CloseOrder($order, 1800));
        return $order;
    }
}
